home *** CD-ROM | disk | FTP | other *** search
/ Cracking 2 / Cracking II..iso / Texty / crackme / SOLUTION.txt < prev    next >
Encoding:
Text File  |  1999-09-15  |  15.8 KB  |  207 lines

  1. Intro Okay, this tutorial will teach the basics of how to crack an encrypted program. We will use a demonstration crackme that I coded. This crackme is a simple nag
  2. elimination. The nag is called from the API MessageBoxA so normal removal would be easy. However, this routine is encrypted and therefore patching would be a
  3. slight problem. I am here to remove this problem and open the world of cracking SMC to the less experienced crackers. 
  4.  
  5. What is encryption? Encryption, is where data is modified in some way to make it unreadable. Within programming this involves having a certain routine, usually at the
  6. start of the program that decrypts the rest of the program before it is executed. 
  7.  
  8. Why use encryption? Applications are usually encrypted to stop reversal of them. If you attempt to decompile an application that is encrypted you are likely to get a
  9. mess of a decompilation that is near impossible to follow and is very little use. 
  10.  
  11. The problem with encryption For the application to run the code must be unencrypted before it is executed. Therefore, it is relativly easy for someone to view the
  12. program in memory while it is running and investigate what is going on. However, a clever program can re-encrypt its data after it has been executed to make
  13. understanding the code a little more difficult. 
  14.  
  15. So what is SMC? SMC stands for 'Self Modifying Code'. This is where an application modifies itself at run-time. An encrypted program is very likely to be use SMC
  16. as it somehow needs to decrypt it's code, which involves rewriting the decrypted program code back into the program, and in essence modifying itself. 
  17.  
  18. Does SMC have any more uses? SMC has many uses within the programming world. However, it is rarely used. SMC code is used within encryption, packers, virii,
  19. but to mention a few common applications. SMC is ideal whenever a program needs to act dynamically to environmental conditions. 
  20.  
  21. Our target
  22.  
  23.  
  24. Now for our application. When we run the program we are encountered with a horrible looking nag screen. Now, as we are all used to programming under Win32 we
  25. recognise this dialog as the same dialog that is called by the function MessageBox. Now what you may not know is that their are two versions of the windows
  26. messagebox function. One is 16bit and is left in windows for the old Win3.1 applications to ensure they still operate successfully, the other is an all new, Win32 veresion.
  27. How do we know which is which? Well, this is relativly simple. The guys at Microsoft descided that some new 32bit calls should have the letter A appended onto the
  28. call name. So, as our application is a Win32 application, we can safely guess that the dialog is displayed using the function MessageBoxA. 
  29.  
  30. Now you're probably worrying about not typing MessageBoxA when you use these message boxes in your application, well thats not a problem either as most high level
  31. compilors automatically use Win32 functions where ever possible. If however, you write your programs in a real language such as Assembly, well, then you have a
  32. problem. 
  33.  
  34. Now we know what function the program uses we can successfully attempt to remove the part of the program that attempts to execute this instruction. To do this
  35. successfully we will need to know what paramaters the function expects, and what return value it gives. So we open up our Windows API reference. Find the details
  36. for the call MessageBox (Yep, the windows guys don't bother diferenciating between Win16 and Win32). You should see the following format for the function: 
  37.  
  38.     int MessageBox(
  39.  
  40.          HWND  hWnd,            // handle of owner window
  41.          LPCTSTR  lpText,       // address of text in message box
  42.          LPCTSTR  lpCaption,    // address of title of message box  
  43.          UINT  uType            // style of message box
  44.     );  
  45.     
  46.  
  47. Now a little Win32asm knowledge is essential. We need to know how this function would be executed in an assembley program. Here is a sample code snippet of an
  48. example of a MessageBox call: 
  49.  
  50.         push    MB_OK                   ;style of message box
  51.         push    offset MsgTitle         ;address of the title of message box
  52.         push    offset MsgText          ;address of text in message box
  53.         push    hwnd                    ;handle of owner window
  54.         call    MessageBoxA             ;execute the function
  55.     
  56.  
  57. You should notice that the paramaters for the function are pushed onto the stack backwards and then the call is executed. What is the stack? The stack is a form of
  58. temporary storage available to programmers. The stack on a LILO basis. Therefore when you attempt to get a value back of the stack you will recieve the last value
  59. you pushed onto the stack. For example: 
  60.  
  61.         push    Value1          ;put our first value on top of the stack
  62.         push    Value2          ;put our second value on top of the previous value in the stack
  63.         Push    Value3          ;put the last value on top of all the others
  64.         
  65.         pop     Value3          ;restore the third value
  66.         pop     Value2          ;restore the second value
  67.         pop     Value1          ;and restore the first value
  68.     
  69.  
  70. Normally, when a windows function is executed it will 'pop' all the paramaters of the stack, leaving your stack as it was before you attempted to push all the functions
  71. paramaters and to call the function. This does, however, depend on the calling convention used, but dont worry. Windows nearly always operates this way. This
  72. knowledge of the stack that i have just given you should be enough for most of your reverse engineering needs. 
  73.  
  74. So know we know all the theory behind the programs displaying of the message box, we need to make our debugger stop at the point in the program where this function
  75. will be executed. I use SoftICE as my debugger, and so should everybody. SoftICE is available from many 'warez' sites throughout the internet and I am informed that
  76. the latest version is 4.01, but have only ever seen and used version 4. I do, however, not condone piracy so you should buy this wonderfull application from
  77. www.numega.com. Trust me, it's worth the money. Enough promotion, lets begin. 
  78.  
  79. Assuming that you have successfully installed SoftICE, and included all the relevant exports (get another tut on this, their are MANY), we need to tell the debugger to
  80. break on execution of the function MessageBoxA. Firstly enter SoftICE by typing 'CTRL-D' from within windows. You should now be presented with the glorious
  81. world of SoftICE. Now type 'BPX MessageBoxA' within SoftICE. You should recieve no error messages. If you do then you've not set up SoftICE correctly. Now we
  82. press 'CTRL-D' again to exit SoftICE and then when back in Windows we go and attempt to execute our application. Allmost as soon as you run the application you
  83. should be poped back into SoftICE. 
  84.  
  85. No we are stuck deep within the depths of the windows code. We want to return to the process that origionally called the function (our target application). We press
  86. 'F12' to return to the section of code that called this and we should be shoved back in windows and see that horrible looking nag screen. If you click on the 'OK' button
  87. you will be back in SoftICE. Below the code window you should see the name of our application looking something like: n0p3x!CODE+### where ### is any sequence
  88. of hexadecimal numbers. Now we are where we want to be. Take a little look around this section of code, go on, it won't bite. Get a REAL feel for the code. By
  89. pressing the 'CTRL' key and the up and down arrows you can scroll up and down the code to your hearts content. 
  90.  
  91. You should see the four values that are pushed onto the stack before the call. If you want to investigate some of the paramaters to ensure that this is the correct
  92. message box you can type 'D [MEMORY ADDRESS]' where message address is the number trailing the push statement. If you do this for the second and third push
  93. you should see both thge message box's title and the message box's text. Okay enough messing around. Wee need to remove this whole call. This means removing all of
  94. the pushes aswell otherwise we will mess up the programs stack and probably crash. 
  95.  
  96. Removing the call is easy. Their is a special command called 'NOP' which when executed does nothing but a small delay. (don't worry, when i say small, I mean
  97. SMALL). We sinply need to replace the whole call and pushes with a few NOP's. However, a NOP is only one byte long, and no doubt our message box takes more
  98. than one byte. We simply replace every byte with a NOP as we don't want to mess up the program size as this will DEFINATELY result in a crash. How do we know
  99. how many bytes our function takes? Well this is quite simple. If you type 'CODE ON' from within SOftICE you will see the opcodes for every command. It should look
  100. a little like this: 
  101.  
  102.         6A00            push    00                      ;the message box stlyle
  103.         6800204000      push    402000                  ;the caption
  104.         6885204000      push    402085                  ;the text
  105.         6A00            push    00                      ;the parent windows handle
  106.         E84E000000      call    USER32!MessageBoxA      
  107.     
  108.  
  109. Don't worry that the parent windows handle is 0. This just means that the message box doesnt have a parent window. One byte is two numbers. So we can tell that the
  110. first push is two bytes and would require replacing by two NOP's. We need to find this section of code in our compiled exe within a hex editor. I use HIEW and Hex
  111. Workshop. Although Hew Workshop is probably better for beginners. Within the hex editor search for the hex byte sequence '6A006800204000'. This is simply the
  112. opcodes for the first two pushes put together. If you found this byte sequence then you should search again to see is the sequence exists elsewhere within the program,
  113. if it does then you need to increase your byte sequence to search for by adding on more opcodes from the rest of the code until you get a nice match. Once you find the
  114. code you would replace every byte above in the EXE with the opcode 90. 90 is the opcode for NOP but i'm pretty sure that most people know this. 
  115.  
  116. However, we have a problem. The byte sequence doesn't exist. What has happened here? Well, the program is encryprted in some way to try and stop you altering it. If
  117. the program wasn't encrypted then you would probably found the bytes okay and the program would work as expected. 
  118.  
  119. It is a safe bet that the program is decrypted before it is executed. We simply need to find out where this happens to see what is going on. Open the program in a
  120. disasembler. I use WinDasm 8.9. You need to find the decryption routine. The routine should look like this: 
  121.  
  122.         mov     reg1, addr-to-write-to
  123.         mov     reg2, [reg1]
  124.         ;manipulate reg2
  125.         mov     [reg1], reg2
  126.     
  127.  
  128. We know that this must be executed before the message box is displayed otherwise the program would attempt to execute the encrypted bytes, which would most likely
  129. crash. We know that the first push is at address 4011b0 in memory. Simply go to that code location in your dissasembler and should see a mass of stupid instructions.
  130. This is the encrypted version of our message box function. If we simply backtrace from this point we will see where the program rewrites this data. If you scroll up a
  131. little you should see the following text: 
  132.  
  133.      *Referenced by a CALL at address:
  134.      :00401020
  135.     
  136.  
  137. So we know where this little message box procedure is called from. Go to that address in your dissasembler. Investigate this new area of code you have landed in.
  138. Notice it is very near to the start of the program and that only four functions are called before our message box. This doesn't leave much room for our decryption to
  139. hide. As three of these functions are windows functions we know the decryption routine should reside in the unnamed call, which, coincidently is called the line before
  140. our message box. Go to the address that the call is pointing at in your dissasembler. You should see a very suspicios looking routine indeed. It should look like this: 
  141.  
  142. * Referenced by a CALL at Address:
  143. |:0040101B   
  144. |
  145. :00401194 B8AB114000              mov eax, 004011AB
  146.  
  147. * Referenced by a (U)nconditional or (C)onditional Jump at Address:
  148. |:004011A8(U)
  149. |
  150. :00401199 8A18                    mov bl, byte ptr [eax]
  151. :0040119B 80F301                  xor bl, 01
  152. :0040119E 8818                    mov byte ptr [eax], bl
  153. :004011A0 40                      inc eax
  154. :004011A1 3DC3114000              cmp eax, 004011C3
  155. :004011A6 7F02                    jg 004011AA
  156. :004011A8 EBEF                    jmp 00401199
  157.  
  158. * Referenced by a (U)nconditional or (C)onditional Jump at Address:
  159. |:004011A6(C)
  160. |
  161. :004011AA C3                      ret
  162.     
  163.  
  164. From your basic knowledge of asm you should be able to translate this whole section of code. But i'll just skim through it to make sure :-). Okay, Firstly the mov eax,
  165. 004011AB moves the address of the start of the encrypted code into a register. Then the mov bl, byte ptr [eax] moves the first byte of the encrypted code into the
  166. register bl. The xor bl, 01 encrypts the byte ;-). The inc eax increments the value in the register eax to point to the next byte of the encrypted routine. The program
  167. then (cmp eax, 004011C3) checks if we are at the end of the encrypted routine, and if we are at a greater address then it (jg 00401AA) jumps to a single ret which
  168. exits the function. If we are not at the end of the encrypted data then we (jmp 00401199) jump back to the start of the code and decrypt the next byte. 
  169.  
  170. So, from this you WILL be able to tell that all the program does is xor each byte with the number 1. This leaves us many ways to attack the application. We could simply
  171. decrypt the bytes by hand and NOP out the whole decryption routine, write encrypted NOP's in place of the message box call, or simple NOP both routines as none of
  172. them perform anything except the nag screen. 
  173.  
  174. We will opt for the harder option. We will inject encrypted code back into the file. As we know where the encrypted code resides in Windasm we can find the offset
  175. easily. If you click on the line of code that the first push should be if it wasn't encrypted the status bar should display an offset. In a hex editor just tell it to go to the
  176. offset. Now, Windasm shows us that the encrypted code looks like: 
  177.  
  178. :004011B0 6B0169                  imul eax, dword ptr [ecx], 00000069
  179. :004011B3 0121                    add dword ptr [ecx], esp
  180. :004011B5 41                      inc ecx
  181. :004011B6 016984                  add dword ptr [ecx-7C], ebp
  182. :004011B9 214101                  and dword ptr [ecx+01], eax
  183. :004011BC 6B01E9                  imul eax, dword ptr [ecx], FFFFFFE9
  184. :004011BF 4F                      dec edi
  185. :004011C0 0101                    add dword ptr [ecx], eax
  186. :004011C2 01C2                    add edx, eax
  187.     
  188.  
  189. All we need to do is to replace each byte with our encrypted NOP. If you do a simple 90h xor 1h you should get the encrypted value for the NOP. It's 91h. Now write
  190. the bytes back to the file in a hex editor. Run the program and voila. It runs minus a nag screen. 
  191.  
  192. Extra notes
  193.  
  194. Unfortunately, most encryption routines are a lot more complex than a simple xor. However, once you know the basics it's all the same theory, just in a more complex
  195. mannor. Sometimes there may be more than one decryption routine. Sometimes the whole program (including resource) will be encrypted. However, all have a
  196. decryption routine. It is though, possible for the deryption routine to be encrypted itself, and to have its own decryption routine, but that will involve the same theory, just
  197. take more time. 
  198.  
  199. Some people may complain about me just using NOP's to patch with. I don't see why. They complain that some programs have CRC checks, but this is very rare in
  200. encrypted programs, besides, any CRC that can be coded, can be cracked :-). Another tut i think :-) 
  201.  
  202. Cheerz,
  203.  
  204. n0p3x
  205.  
  206. EMAIL: adminno1@yahoo.com
  207. WEB: http://cod3r.cjb.net